package com.zkh.myframe.database;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.sql.DataSource;

import com.zkh.myframe.annotation.IgnoreField;
import com.zkh.myframe.annotation.Table;
import com.zkh.myutils.annotation.Column;
import com.zkh.myutils.database.DatabaseException;
import com.zkh.myutils.database.conutils.PoolUtils;
import com.zkh.myutils.io.IOUtils;
import com.zkh.myutils.utils.ArrayUtils;
import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.StringUtils;

/**
 * 实体相关工具类
 * @author zkh
 */
class EntityUtils {
	
	//表及数据元信息映射
	private static Map<String, List<String>> tableMetaDataMap = new HashMap<>();
	//实体和Method映射
	private static Map<Class<?>, List<EntityField>> clsGetterMap = new HashMap<>();
	
	/**
	 * 获取字段值
	 * @param dataSource 数据源
	 * @param entityList 实体集合 
	 * @param clazz 实体Class
	 * @param tablePrefix 表前缀
	 */
	public static <T> List<List<EntityField>> getFieldValue(DataSource dataSource, List<T> entityList, Class<?> clazz, Table tbl, String tablePrefix) {
		//结果容器
		List<List<EntityField>> ctn = new ArrayList<>();
		//获取字段Getter
		List<EntityField> fieldList = clsGetterMap.get(clazz);
		//为空
		if(fieldList==null) {
			//初始化容器
			fieldList = initFieldList(dataSource, clazz, tbl, tablePrefix);
		}
		//获取值
		for(T t: entityList) {
			//子容器
			List<EntityField> efList = new ArrayList<>();
			//遍历
			for(EntityField field: fieldList) {
				//获取值
				try {
					Object val = field.getGetter().invoke(t);
					//初始化返回对象
					EntityField ef = new EntityField(field, val);
					//保存
					efList.add(ef);
				} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
					throw new DatabaseException("调用" + field.getFieldName() + "字段的getter方法发生异常", e);
				}
			}
			ctn.add(efList);
		}
		//返回最终结果
		return ctn;
	}
	
	/**
	 * 初始化字段列表
	 * @param dataSource 数据源
	 * @param clazz 实体类Class
	 * @param tablePrefix 表前缀
	 */
	private synchronized static List<EntityField> initFieldList(DataSource dataSource, Class<?> clazz, Table tbl, String tablePrefix) {
		//临时容器
		List<EntityField> tmpList = new ArrayList<>(), commonList = new ArrayList<>(), pkList = new ArrayList<>();
		//主键
		List<String> pks = ArrayUtils.asList(tbl.keys());
		//获取元数据
		List<String> metaDataList = getTableMetaData(dataSource, tbl.name(), tablePrefix);
		//字段
		Map<String, Field> fieldMap = Arrays.stream(clazz.getDeclaredFields()).collect(
				Collectors.toMap(f->Assert.getIfNotNull(f.getAnnotation(Column.class), v->v.name(), ()->f.getName()).toLowerCase(), f->f));
		//遍历元数据（以元数据为准，而不是实体对象）
		for(String fname: metaDataList) {
			//存在
			if(fieldMap.containsKey(fname)) {
				//获取字段
				Field field = fieldMap.get(fname);
				//忽略注解
				IgnoreField ignore = field.getAnnotation(IgnoreField.class);
				//不忽略
				if(ignore==null || (!ignore.insert() || !ignore.update())) {
					//获取Method
					Method method = EntityUtils.getGetter(field.getName(), clazz);
					//字段名
					String fieldName = Assert.getIfNotNull(field.getAnnotation(Column.class), v->v.name(), ()->field.getName());
					//初始化返回对象
					EntityField ef = new EntityField(fieldName, null, pks.contains(fieldName));
					//处理忽略标识
					ef.setInsert(ignore==null || !ignore.insert());
					ef.setUpdate(ignore==null || !ignore.update());
					ef.setGetter(method);
					//保存
					(ef.isPrimaryKey() ? pkList : commonList).add(ef);
				}
			}
		}
		//整合。主键参数放最后，避免参数混乱
		tmpList.addAll(commonList);tmpList.addAll(pkList);
		//保存全局变量
		clsGetterMap.put(clazz, tmpList);
		//返回数据
		return tmpList;
	}
	
	/**
	 * 获取数据表的元数据
	 * @param dataSource 数据源
	 * @param tablename 表名
	 */
	private static List<String> getTableMetaData(DataSource dataSource, String tablename, String tablePrefix){
		//元数据集合
		List<String> metaDataList = tableMetaDataMap.get(tablename);
		//为空
		if(metaDataList==null) {
			//获取
			synchronized (tablename.intern()) {
				//临时容器
				List<String> tempMetaDataList = new ArrayList<>();
				//初始化连接对象，预处理对象和结果集对象
				Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;
				try{
					//数据源获取连接
					conn = dataSource.getConnection();
					//查询数据表表头数据
					String sql = "select * from " + (StringUtils.isEmpty(tablePrefix) ? "" : tablePrefix + ".") + tablename + " where 1=2";
					//预处理语句
					pstmt = conn.prepareStatement(sql);
					//结果集
					rs = pstmt.executeQuery();
					//结果集元数据
					ResultSetMetaData meta = rs.getMetaData();
					//获取长度
					int length = meta.getColumnCount();
					//遍历并添加到集合
					for(int i=1;i<=length;i++){
						tempMetaDataList.add(meta.getColumnName(i).toLowerCase());
					}
				}catch(SQLException e){
					throw new DatabaseException(e.getMessage());
				}finally{
					//关闭流
					PoolUtils.close(conn);
					IOUtils.closeQuietly(rs, e->new DatabaseException(e.getMessage()));
					IOUtils.closeQuietly(pstmt, e->new DatabaseException(e.getMessage()));
				}
				//保存
				tableMetaDataMap.put(tablename, tempMetaDataList);
				metaDataList = tempMetaDataList;
			}
		}
		//返回值
		return metaDataList;
	}

	/**
	 * 获取字段的getter
	 * @param fieldName 字段名
	 * @param clazz 所属类
	 */
	private static Method getGetter(String fieldName, Class<?> clazz) {
		//getter
		String t = StringUtils.uppercaseFirst(fieldName);
		String getter = "get" + t, isGetter = "is" + t;
		try {
			//获取Method并返回
			return clazz.getMethod(getter);
		} catch (NoSuchMethodException e) {
			try {
				//获取Method并返回
				return clazz.getMethod(isGetter);
			} catch (NoSuchMethodException e1) {
				throw new DatabaseException("字段" + fieldName + "不存在对应的getter");
			} catch (SecurityException e1) {
				throw new DatabaseException(e1);
			}
		} catch (SecurityException e) {
			throw new DatabaseException(e);
		}
	}
	
	/**
	 * 实体字段辅助类
	 * @author zkh
	 */
	static class EntityField{
		//名称
		private String fieldName;
		//值
		private Object fieldValue;
		//主键
		private boolean primaryKey;
		//新增
		private boolean insert;
		//更新
		private boolean update;
		
		private Method getter;
		
		//构造方法
		public EntityField(String fieldName, Object fieldValue, boolean primaryKey) {
			this.fieldName = fieldName;
			this.primaryKey = primaryKey;
			this.fieldValue = fieldValue;
		}
		
		//构造方法
		public EntityField(EntityField ef, Object value) {
			this.fieldName = ef.getFieldName();
			this.primaryKey = ef.isPrimaryKey();
			this.fieldValue = value;
			this.insert = ef.insert;
			this.update = ef.update;
		}
		
		public String getFieldName() {
			return fieldName;
		}
		public void setFieldName(String fieldName) {
			this.fieldName = fieldName;
		}
		public boolean isPrimaryKey() {
			return primaryKey;
		}
		public void setPrimaryKey(boolean primaryKey) {
			this.primaryKey = primaryKey;
		}
		public Object getFieldValue() {
			return fieldValue;
		}
		public void setFieldValue(Object fieldValue) {
			this.fieldValue = fieldValue;
		}
		public boolean isInsert() {
			return insert;
		}
		public void setInsert(boolean insert) {
			this.insert = insert;
		}
		public boolean isUpdate() {
			return update;
		}
		public void setUpdate(boolean update) {
			this.update = update;
		}
		public Method getGetter() {
			return getter;
		}
		public void setGetter(Method getter) {
			this.getter = getter;
		}
	}
}
